home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
X11
/
xantfarm
/
xantfarm.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-20
|
34KB
|
1,238 lines
/* xantfarm - Insect world - build it up, tear it down.
**
** Copyright (C) 1991 by Jef Poskanzer
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
*/
#ifndef lint
static char rcsid[] =
"@(#) $Header: xantfarm.c,v 1.8 91/10/16 21:39:24 jef Exp $";
#endif
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
/* Add fd_set definitions, in case the system doesn't have them. */
#ifndef FD_SET
#define NFDBITS 32
#define FD_SETSIZE 32
#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
#endif
/* Sand bitmap. */
#define sand_width 4
#define sand_height 4
static char sand_bits[] = {0x0a,0x05,0x0a,0x05};
/* Ant bitmaps. */
#define ant_ld0_width 12
#define ant_ld0_height 4
static char ant_ld0_bits[] = {0x03,0x00,0xec,0x07,0xff,0x07,0x20,0x09};
#define ant_ld1_width 12
#define ant_ld1_height 4
static char ant_ld1_bits[] = {0x03,0x00,0xec,0x07,0xff,0x07,0x90,0x04};
#define ant_lu0_width 12
#define ant_lu0_height 4
static char ant_lu0_bits[] = {0x20,0x09,0xff,0x07,0xec,0x07,0x03,0x00};
#define ant_lu1_width 12
#define ant_lu1_height 4
static char ant_lu1_bits[] = {0x90,0x04,0xff,0x07,0xec,0x07,0x03,0x00};
#define ant_rd0_width 12
#define ant_rd0_height 4
static char ant_rd0_bits[] = {0x00,0x0c,0x7e,0x03,0xfe,0x0f,0x49,0x00};
#define ant_rd1_width 12
#define ant_rd1_height 4
static char ant_rd1_bits[] = {0x00,0x0c,0x7e,0x03,0xfe,0x0f,0x92,0x00};
#define ant_ru0_width 12
#define ant_ru0_height 4
static char ant_ru0_bits[] = {0x49,0x00,0xfe,0x0f,0x7e,0x03,0x00,0x0c};
#define ant_ru1_width 12
#define ant_ru1_height 4
static char ant_ru1_bits[] = {0x92,0x00,0xfe,0x0f,0x7e,0x03,0x00,0x0c};
#define ant_ur0_width 4
#define ant_ur0_height 12
static char ant_ur0_bits[] = {
0x05,0x05,0x06,0x06,0x04,0x0e,0x06,0x06,0x0e,0x06,0x06,0x08};
#define ant_ur1_width 4
#define ant_ur1_height 12
static char ant_ur1_bits[] = {
0x05,0x05,0x06,0x06,0x0c,0x06,0x06,0x0e,0x06,0x06,0x0e,0x00};
#define ant_ul0_width 4
#define ant_ul0_height 12
static char ant_ul0_bits[] = {
0x0a,0x0a,0x06,0x06,0x02,0x07,0x06,0x06,0x07,0x06,0x06,0x01};
#define ant_ul1_width 4
#define ant_ul1_height 12
static char ant_ul1_bits[] = {
0x0a,0x0a,0x06,0x06,0x03,0x06,0x06,0x07,0x06,0x06,0x07,0x00};
#define ant_dr0_width 4
#define ant_dr0_height 12
static char ant_dr0_bits[] = {
0x08,0x06,0x06,0x0e,0x06,0x06,0x0e,0x04,0x06,0x06,0x05,0x05};
#define ant_dr1_width 4
#define ant_dr1_height 12
static char ant_dr1_bits[] = {
0x00,0x0e,0x06,0x06,0x0e,0x06,0x06,0x0c,0x06,0x06,0x05,0x05};
#define ant_dl0_width 4
#define ant_dl0_height 12
static char ant_dl0_bits[] = {
0x01,0x06,0x06,0x07,0x06,0x06,0x07,0x02,0x06,0x06,0x0a,0x0a};
#define ant_dl1_width 4
#define ant_dl1_height 12
static char ant_dl1_bits[] = {
0x00,0x07,0x06,0x06,0x07,0x06,0x06,0x03,0x06,0x06,0x0a,0x0a};
#define antc_ld0_width 12
#define antc_ld0_height 4
static char antc_ld0_bits[] = {0x03,0x00,0xed,0x07,0xff,0x07,0x20,0x09};
#define antc_ld1_width 12
#define antc_ld1_height 4
static char antc_ld1_bits[] = {0x03,0x00,0xed,0x07,0xff,0x07,0x90,0x04};
#define antc_lu0_width 12
#define antc_lu0_height 4
static char antc_lu0_bits[] = {0x20,0x09,0xff,0x07,0xed,0x07,0x03,0x00};
#define antc_lu1_width 12
#define antc_lu1_height 4
static char antc_lu1_bits[] = {0x90,0x04,0xff,0x07,0xed,0x07,0x03,0x00};
#define antc_rd0_width 12
#define antc_rd0_height 4
static char antc_rd0_bits[] = {0x00,0x0c,0x7e,0x0b,0xfe,0x0f,0x49,0x00};
#define antc_rd1_width 12
#define antc_rd1_height 4
static char antc_rd1_bits[] = {0x00,0x0c,0x7e,0x0b,0xfe,0x0f,0x92,0x00};
#define antc_ru0_width 12
#define antc_ru0_height 4
static char antc_ru0_bits[] = {0x49,0x00,0xfe,0x0f,0x7e,0x0b,0x00,0x0c};
#define antc_ru1_width 12
#define antc_ru1_height 4
static char antc_ru1_bits[] = {0x92,0x00,0xfe,0x0f,0x7e,0x0b,0x00,0x0c};
#define antc_ur0_width 4
#define antc_ur0_height 12
static char antc_ur0_bits[] = {
0x07,0x05,0x06,0x06,0x04,0x0e,0x06,0x06,0x0e,0x06,0x06,0x08};
#define antc_ur1_width 4
#define antc_ur1_height 12
static char antc_ur1_bits[] = {
0x07,0x05,0x06,0x06,0x0c,0x06,0x06,0x0e,0x06,0x06,0x0e,0x00};
#define antc_ul0_width 4
#define antc_ul0_height 12
static char antc_ul0_bits[] = {
0x0e,0x0a,0x06,0x06,0x02,0x07,0x06,0x06,0x07,0x06,0x06,0x01};
#define antc_ul1_width 4
#define antc_ul1_height 12
static char antc_ul1_bits[] = {
0x0e,0x0a,0x06,0x06,0x03,0x06,0x06,0x07,0x06,0x06,0x07,0x00};
#define antc_dr0_width 4
#define antc_dr0_height 12
static char antc_dr0_bits[] = {
0x08,0x06,0x06,0x0e,0x06,0x06,0x0e,0x04,0x06,0x06,0x05,0x07};
#define antc_dr1_width 4
#define antc_dr1_height 12
static char antc_dr1_bits[] = {
0x00,0x0e,0x06,0x06,0x0e,0x06,0x06,0x0c,0x06,0x06,0x05,0x07};
#define antc_dl0_width 4
#define antc_dl0_height 12
static char antc_dl0_bits[] = {
0x01,0x06,0x06,0x07,0x06,0x06,0x07,0x02,0x06,0x06,0x0a,0x0e};
#define antc_dl1_width 4
#define antc_dl1_height 12
static char antc_dl1_bits[] = {
0x00,0x07,0x06,0x06,0x07,0x06,0x06,0x03,0x06,0x06,0x0a,0x0e};
/* Definitions. */
#define RANDOM_DIG_PROB 200 /* dig down while wandering */
#define RANDOM_DROP_PROB 200 /* drop while wandering */
#define RANDOM_TURN_PROB 200 /* turn while wandering */
#define CONCAVE_BELOW_DIG_PROB 10 /* dig a concave corner below ground*/
#define CONVEX_ABOVE_DROP_PROB 10 /* drop at convex corner above ground */
#define CALM_PROB 100 /* calm down from a panic */
#define COMPACT 15 /* this depth of sand turns to dirt */
#define DIRT_START_FRAC 0.666666
#define GRID_SIZE sand_width
#define ANT_GRIDS ( ant_lu0_width / GRID_SIZE )
/* The three elements. */
#define E_AIR 0
#define E_DIRT 1
#define E_SAND 2
typedef struct ant_struct {
int x, y;
int dir;
int behavior;
int timer;
int phase;
} ant;
/* The eight directions. */
#define D_LEFT_DOWN 0
#define D_LEFT_UP 1
#define D_RIGHT_DOWN 2
#define D_RIGHT_UP 3
#define D_UP_RIGHT 4
#define D_UP_LEFT 5
#define D_DOWN_RIGHT 6
#define D_DOWN_LEFT 7
#define N_DIRS 8
#define N_ALTPIXMAPS 2
/* The three behaviors. */
#define B_WANDERING 0
#define B_CARRYING 1
#define B_PANIC 2
/* Timing factors for the three behaviors. */
#define T_WANDERING 4
#define T_CARRYING 5
#define T_PANIC 1
typedef struct falling_sand_struct {
int x, y;
int active;
} falling_sand;
/* Externals. */
extern char* malloc();
extern char* realloc();
extern long time();
extern long random();
/* Forward routines. */
static void x_init();
static Window VirtualRootWindowOfScreen();
static void ant_init();
static void main_loop();
static void expose();
static void paint_run();
static void poke();
static void invalidate();
static void invalidate_ant();
static void cleanup();
static void moveants();
static void move();
static void turn();
static int legal_dir();
static int try_dig();
static void loosen_neighbors();
static void loosen_one();
static void drop();
static void behave();
static void sand_fall();
/* Variables. */
static char* argv0;
static Display* display;
static Screen* screen;
static Window root;
static int root_w, root_h, root_d;
static unsigned long foreground, background;
static GC airgc;
static GC sandgc;
static Pixmap sand_pixmap;
static GC antgc;
static char* ant_bits[N_DIRS][N_ALTPIXMAPS] = {
ant_ld0_bits, ant_ld1_bits, ant_lu0_bits, ant_lu1_bits,
ant_rd0_bits, ant_rd1_bits, ant_ru0_bits, ant_ru1_bits,
ant_ur0_bits, ant_ur1_bits, ant_ul0_bits, ant_ul1_bits,
ant_dr0_bits, ant_dr1_bits, ant_dl0_bits, ant_dl1_bits };
static char* antc_bits[N_DIRS][N_ALTPIXMAPS] = {
antc_ld0_bits, antc_ld1_bits, antc_lu0_bits, antc_lu1_bits,
antc_rd0_bits, antc_rd1_bits, antc_ru0_bits, antc_ru1_bits,
antc_ur0_bits, antc_ur1_bits, antc_ul0_bits, antc_ul1_bits,
antc_dr0_bits, antc_dr1_bits, antc_dl0_bits, antc_dl1_bits };
static int ant_width[N_DIRS] = {
ant_ld0_width, ant_lu0_width, ant_rd0_width, ant_ru0_width,
ant_ur0_width, ant_ul0_width, ant_dr0_width, ant_dl0_width };
static int ant_height[N_DIRS] = {
ant_ld0_height, ant_lu0_height, ant_rd0_height, ant_ru0_height,
ant_ur0_height, ant_ul0_height, ant_dr0_height, ant_dl0_height };
static Pixmap ant_pixmap[N_DIRS][N_ALTPIXMAPS];
static Pixmap antc_pixmap[N_DIRS][N_ALTPIXMAPS];
static int num_exposerects, max_exposerects;
static XRectangle* exposerects;
static int cycles;
static int world_w, world_h;
static int surface;
static unsigned char** world;
static ant* ants;
static int num_ants;
static int dx[N_DIRS] = { -1, -1, 1, 1, 0, 0, 0, 0 };
static int dy[N_DIRS] = { 0, 0, 0, 0, -1, -1, 1, 1 };
static int foot_dir[N_DIRS] = {
D_DOWN_RIGHT, D_UP_RIGHT, D_DOWN_LEFT, D_UP_LEFT,
D_RIGHT_DOWN, D_LEFT_DOWN, D_RIGHT_UP, D_LEFT_UP };
static int back_dir[N_DIRS] = {
D_UP_LEFT, D_DOWN_LEFT, D_UP_RIGHT, D_DOWN_RIGHT,
D_LEFT_UP, D_RIGHT_UP, D_LEFT_DOWN, D_RIGHT_DOWN };
#define D_LEFT_DOWN 0
#define D_LEFT_UP 1
#define D_RIGHT_DOWN 2
#define D_RIGHT_UP 3
#define D_UP_RIGHT 4
#define D_UP_LEFT 5
#define D_DOWN_RIGHT 6
#define D_DOWN_LEFT 7
static int num_falling_sands, max_falling_sands;
static falling_sand* falling_sands;
/* Routines. */
void
main( argc, argv )
int argc;
char* argv[];
{
int printpid;
char* display_name;
int synchronous;
int pid, tty;
/* Parse args. */
argv0 = argv[0];
num_ants = 10;
cycles = 15;
printpid = 0;
display_name = (char*) 0;
synchronous = 0;
for( ; ; )
{
if ( argc >= 3 && strcmp( argv[1], "-n" ) == 0 )
{
++argv; --argc;
num_ants = atoi( argv[1] );
if ( num_ants <= 0 )
goto usage;
++argv; --argc;
continue;
}
if ( argc >= 3 && strcmp( argv[1], "-c" ) == 0 )
{
++argv; --argc;
cycles = atoi( argv[1] );
if ( cycles < 0 )
goto usage;
++argv; --argc;
continue;
}
if ( argc >= 2 && strcmp( argv[1], "-i" ) == 0 )
{
++argv; --argc;
printpid = 1;
continue;
}
if ( argc >= 3 && (
strcmp( argv[1], "-display" ) == 0 ||
strcmp( argv[1], "-displa" ) == 0 ||
strcmp( argv[1], "-displ" ) == 0 ||
strcmp( argv[1], "-disp" ) == 0 ||
strcmp( argv[1], "-dis" ) == 0 ||
strcmp( argv[1], "-di" ) == 0 ||
strcmp( argv[1], "-d" ) == 0 ) )
{
++argv; --argc;
display_name = argv[1];
++argv; --argc;
continue;
}
if ( argc >= 2 && (
strcmp( argv[1], "-sync" ) == 0 ||
strcmp( argv[1], "-syn" ) == 0 ||
strcmp( argv[1], "-sy" ) == 0 ||
strcmp( argv[1], "-s" ) == 0 ) )
{
++argv; --argc;
synchronous = 1;
continue;
}
break;
}
if ( argc > 1 )
{
usage:
(void) fprintf(
stderr, "usage: %s [-n num] [-c cycles] [-i] [-display name]\n", argv0 );
exit( 1 );
}
/* Initialize the random number generator. */
srandom( (int) ( time( (long*) 0 ) ^ getpid() ) );
/* Set up X stuff. */
x_init( display_name, synchronous );
/* Create the ant world. */
ant_init();
/* Fork, if necessary. */
if ( printpid )
{
pid = fork();
if ( pid < 0 )
{
perror( "fork" );
exit( 1 );
}
else if ( pid > 0 )
{
/* Parent just exits. */
exit( 0 );
}
(void) printf( "%d\n", getpid() );
(void) fflush( stdout );
/* Go stealth (ditch our controlling tty). */
tty = open( "/dev/tty", 0 );
if ( tty < 0 )
{
(void) fprintf( stderr, "%s: ", argv0 );
perror( "/dev/tty open" );
exit( 1 );
}
#if 0
else
{
if ( ioctl( tty, TIOCNOTTY, 0 ) < 0 )
{
(void) fprintf( stderr, "%s: ", argv0 );
perror( "TIOCNOTTY ioctl" );
exit( 1 );
}
(void) close( tty );
}
#endif
}
/* Main loop. */
main_loop();
/*NOTREACHED*/
}
static void
x_init( display_name, synchronous )
char* display_name;
int synchronous;
{
int i, j;
display = XOpenDisplay( display_name );
if ( display == (Display*) 0 )
{
(void) fprintf(
stderr, "%s: can't open display \"%s\"\n", argv0,
XDisplayName( display_name ) );
exit( 1 );
}
if ( synchronous )
XSynchronize( display, 1 );
screen = DefaultScreenOfDisplay( display );
root = VirtualRootWindowOfScreen( screen );
root_w = WidthOfScreen( screen );
root_h = HeightOfScreen( screen );
root_d = DefaultDepthOfScreen( screen );
foreground = BlackPixelOfScreen( screen );
background = WhitePixelOfScreen( screen );
/* Air is all white, so its GC should paint white. */
airgc = XCreateGC( display, root, 0, (XGCValues*) 0 );
XSetForeground( display, airgc, background );
XSetBackground( display, airgc, foreground );
/* Sand is a pixmap, so its GC tiles. */
sandgc = XCreateGC( display, root, 0, (XGCValues*) 0 );
XSetForeground( display, sandgc, foreground );
XSetBackground( display, sandgc, background );
XSetFillStyle( display, sandgc, FillTiled );
sand_pixmap = XCreatePixmapFromBitmapData(
display, root, sand_bits, sand_width, sand_height,
foreground, background, root_d );
XSetTile( display, sandgc, sand_pixmap );
/* Ants paint black through a clipping bitmap. */
antgc = XCreateGC( display, root, 0, (XGCValues*) 0 );
XSetForeground( display, antgc, foreground );
XSetBackground( display, antgc, background );
for ( i = 0; i < N_DIRS; ++i )
for ( j = 0; j < N_ALTPIXMAPS; ++j )
{
ant_pixmap[i][j] = XCreateBitmapFromData(
display, root, ant_bits[i][j], ant_width[i], ant_height[i] );
antc_pixmap[i][j] = XCreateBitmapFromData(
display, root, antc_bits[i][j], ant_width[i], ant_height[i] );
}
num_exposerects = max_exposerects = 0;
}
/* From vroot.h by Andreas Stolcke. */
static Window
VirtualRootWindowOfScreen( screen )
Screen* screen;
{
static Screen* save_screen = (Screen*) 0;
static Window root = (Window) 0;
if ( screen != save_screen )
{
Display* dpy = DisplayOfScreen( screen );
Atom __SWM_VROOT = None;
int i;
Window rootReturn, parentReturn;
Window* children;
unsigned int numChildren;
root = RootWindowOfScreen( screen );
/* Go look for a virtual root. */
__SWM_VROOT = XInternAtom( dpy, "__SWM_VROOT", False );
if ( XQueryTree(
dpy, root, &rootReturn, &parentReturn, &children,
&numChildren ) )
{
for ( i = 0; i < numChildren; ++i)
{
Atom actual_type;
int actual_format;
unsigned long nitems, bytesafter;
Window* newRoot = (Window*) 0;
if ( XGetWindowProperty(
dpy, children[i], __SWM_VROOT, 0, 1, False, XA_WINDOW,
&actual_type, &actual_format, &nitems, &bytesafter,
(unsigned char**) &newRoot ) == Success && newRoot )
{
root = *newRoot;
break;
}
}
if ( children )
XFree( (char*) children );
}
save_screen = screen;
}
return root;
}
static void
ant_init()
{
int x, y, a;
world_w = root_w / GRID_SIZE;
world_h = root_h / GRID_SIZE;
world = (unsigned char**) malloc(
(unsigned) ( world_h * sizeof(unsigned char*) ) );
if ( world == (unsigned char**) 0 )
{
(void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 );
}
for ( y = 0; y < world_h; ++y )
{
world[y] = (unsigned char*) malloc(
(unsigned) ( world_w * sizeof(unsigned char) ) );
if ( world[y] == (unsigned char*) 0 )
{
(void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 );
}
}
surface = world_h * ( 1.0 - DIRT_START_FRAC );
for ( y = 0; y < surface; ++y )
for ( x = 0; x < world_w; ++x )
world[y][x] = E_AIR;
for ( ; y < world_h; ++y )
for ( x = 0; x < world_w; ++x )
world[y][x] = E_DIRT;
ants = (ant*) malloc( (unsigned) ( num_ants * sizeof(ant) ) );
if ( ants == (ant*) 0 )
{
(void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 );
}
for ( a = 0; a < num_ants; ++a )
{
ants[a].x = random() % world_w;
ants[a].y = surface - 1;
ants[a].dir = random() % 2 == 1 ? D_LEFT_DOWN : D_RIGHT_DOWN;
behave( a, B_WANDERING, T_WANDERING );
ants[a].phase = 0;
}
num_falling_sands = max_falling_sands = 0;
}
static void
main_loop()
{
int fd, i;
fd_set fds;
struct timeval timeout;
XEvent ev;
XSelectInput( display, root, ExposureMask | PointerMotionMask );
invalidate( 0, 0, world_w, world_h );
FD_ZERO( &fds );
fd = ConnectionNumber( display );
for (;;)
{
if ( num_exposerects != 0 )
{
for ( i = 0; i < num_exposerects; ++i )
expose(
exposerects[i].x, exposerects[i].y,
(int) exposerects[i].width, (int) exposerects[i].height );
num_exposerects = 0;
continue;
}
if ( cycles != 0 && XPending( display ) == 0 )
{
/* No X events to handle, so wait for a while. */
FD_SET( fd, &fds );
timeout.tv_sec = 1 / cycles;
timeout.tv_usec = 1000000L / cycles;
(void) select( fd + 1, &fds, (int*) 0, (int*) 0, &timeout );
}
if ( XPending( display ) == 0 )
{
/* Still no X events, so let's move. */
moveants();
if ( num_falling_sands > 0 )
sand_fall();
continue;
}
/* Now there are X events. */
XNextEvent( display, &ev );
switch ( ev.type )
{
case Expose:
expose(
ev.xexpose.x, ev.xexpose.y,
ev.xexpose.width, ev.xexpose.height );
break;
case MotionNotify:
poke( ev.xmotion.x, ev.xmotion.y );
break;
}
}
}
static void
expose( ex, ey, ew, eh )
int ex, ey, ew, eh;
{
int x0, y0, x1, y1, x, y, a, d, ax, ay;
int run_start, run_count, run_type;
/* Convert to ant world coordinates. */
x0 = ex / GRID_SIZE;
if ( x0 < 0 ) x0 = 0;
y0 = ey / GRID_SIZE;
if ( y0 < 0 ) y0 = 0;
x1 = ( ex + ew - 1 ) / GRID_SIZE;
if ( x1 >= world_w ) x1 = world_w - 1;
y1 = ( ey + eh - 1 ) / GRID_SIZE;
if ( y1 >= world_h ) y1 = world_h - 1;
/* Paint the ant world. */
for ( y = y0; y <= y1; ++y )
{
/* Collect up a run of identical elements, so we can paint them
** all at once and save oodles of cycles.
*/
run_start = x0;
run_count = 1;
run_type = world[y][x0];
for ( x = x0 + 1; x <= x1; ++x )
{
if ( world[y][x] == run_type )
++run_count;
else
{
paint_run( run_start, run_count, run_type, y );
run_start = x;
run_count = 1;
run_type = world[y][x];
}
}
if ( run_count > 0 )
paint_run( run_start, run_count, run_type, y );
}
/* Now paint any ants in the exposed area. */
for ( a = 0; a < num_ants; ++a )
{
if ( ants[a].x + ANT_GRIDS / 2 >= x0 &&
ants[a].x - ANT_GRIDS / 2 <= x1 &&
ants[a].y + ANT_GRIDS / 2 >= y0 &&
ants[a].y - ANT_GRIDS / 2 <= y1 )
{
d = ants[a].dir;
ax = ants[a].x * GRID_SIZE - ant_width[d] / 2 + GRID_SIZE / 2;
ay = ants[a].y * GRID_SIZE - ant_height[d] / 2 + GRID_SIZE / 2;
if ( ants[a].behavior == B_CARRYING )
XSetClipMask( display, antgc, antc_pixmap[d][ants[a].phase] );
else
XSetClipMask( display, antgc, ant_pixmap[d][ants[a].phase] );
XSetClipOrigin( display, antgc, ax, ay );
XFillRectangle(
display, root, antgc, ax, ay, ant_width[d], ant_height[d] );
}
}
}
static void
paint_run( run_start, run_count, run_type, y )
int run_start, run_count, run_type, y;
{
switch ( run_type )
{
case E_AIR:
XFillRectangle(
display, root, airgc, run_start * GRID_SIZE, y * GRID_SIZE,
run_count * GRID_SIZE, GRID_SIZE );
break;
case E_DIRT:
/* Dirt shows as the default root pattern. */
XClearArea(
display, root, run_start * GRID_SIZE, y * GRID_SIZE,
run_count * GRID_SIZE, GRID_SIZE, False );
break;
case E_SAND:
XFillRectangle(
display, root, sandgc, run_start * GRID_SIZE, y * GRID_SIZE,
run_count * GRID_SIZE, GRID_SIZE );
break;
}
}
static void
poke( px, py )
int px, py;
{
int x, y, a, nx, ny;
x = px / GRID_SIZE;
y = py / GRID_SIZE;
for ( a = 0; a < num_ants; ++a )
{
if ( x >= ants[a].x - ANT_GRIDS / 2 && x <= ants[a].x + ANT_GRIDS / 2 &&
y >= ants[a].y - ANT_GRIDS / 2 && y <= ants[a].y + ANT_GRIDS / 2 )
{
if ( ants[a].behavior == B_CARRYING )
drop( a );
nx = ants[a].x + random() % 3 - 1;
ny = ants[a].y + random() % 3 - 1;
if ( nx < 0 ) nx = 0;
if ( ny < 0 ) ny = 0;
if ( nx >= world_w ) nx = world_w - 1;
if ( ny >= world_h ) ny = world_h - 1;
invalidate_ant( a );
ants[a].x = nx;
ants[a].y = ny;
ants[a].dir = random() % N_DIRS;
invalidate_ant( a );
behave( a, B_PANIC, T_PANIC );
}
}
}
static void
invalidate( x, y, w, h )
int x, y, w, h;
{
int i, ex, ey, ew, eh;;
x *= GRID_SIZE;
y *= GRID_SIZE;
w *= GRID_SIZE;
h *= GRID_SIZE;
/* Check if this rectangle intersects an existing one. */
for ( i = 0; i < num_exposerects; ++i )
{
ex = exposerects[i].x;
ey = exposerects[i].y;
ew = exposerects[i].width;
eh = exposerects[i].height;
if ( x < ex + ew && ex < x + w && y < ey + eh && ey < y + h )
{
/* Found an intersection - merge them. */
if ( x + w > ex + ew )
exposerects[i].width = ew = x + w - ex;
if ( y + h > ey + eh )
exposerects[i].height = eh = y + h - ey;
if ( x < ex )
{
exposerects[i].width = ex + ew - x;
exposerects[i].x = x;
}
if ( y < ey )
{
exposerects[i].height = ey + eh - y;
exposerects[i].y = y;
}
return;
}
}
/* Nope, add a new XRectangle. */
if ( num_exposerects == max_exposerects )
{
if ( max_exposerects == 0 )
{
max_exposerects = 20;
exposerects = (XRectangle*) malloc(
(unsigned) ( max_exposerects * sizeof(XRectangle) ) );
}
else
{
max_exposerects *= 2;
exposerects = (XRectangle*) realloc(
(char*) exposerects,
(unsigned) ( max_exposerects * sizeof(XRectangle) ) );
}
if ( exposerects == (XRectangle*) 0 )
{
(void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 );
}
}
exposerects[num_exposerects].x = x;
exposerects[num_exposerects].y = y;
exposerects[num_exposerects].width = w;
exposerects[num_exposerects].height = h;
++num_exposerects;
}
static void
invalidate_ant( a )
int a;
{
invalidate(
ants[a].x - ANT_GRIDS / 2, ants[a].y - ANT_GRIDS / 2,
ANT_GRIDS, ANT_GRIDS );
}
static void
cleanup()
{
int i, j;
XFreePixmap( display, sand_pixmap );
for ( i = 0; i < N_DIRS; ++i )
for ( j = 0; j < N_ALTPIXMAPS; ++j )
{
XFreePixmap( display, ant_pixmap[i][j] );
XFreePixmap( display, antc_pixmap[i][j] );
}
XFreeGC( display, airgc );
XFreeGC( display, sandgc );
XFreeGC( display, antgc );
XCloseDisplay( display );
}
static void
moveants()
{
int a, x, y, fx, fy;
for ( a = 0; a < num_ants; ++a )
{
--ants[a].timer;
if ( ants[a].timer <= 0 )
{
ants[a].phase = ( ants[a].phase + 1 ) % N_ALTPIXMAPS;
/* Gravity check. */
x = ants[a].x;
y = ants[a].y;
fx = x + dx[foot_dir[ants[a].dir]];
fy = y + dy[foot_dir[ants[a].dir]];
if ( fx >= 0 && fx < world_w && fy >= 0 && fy < world_h &&
world[fy][fx] == E_AIR )
{
/* Whoops, whatever we were walking on disappeared. */
if ( y + 1 < world_h && world[y + 1][x] == E_AIR )
{
invalidate_ant( a );
ants[a].y = y + 1;
invalidate_ant( a );
}
else
/* Can't fall? Try turning. */
turn( a );
}
else
{
/* Ok, the ant gets to do something. */
switch ( ants[a].behavior )
{
case B_WANDERING:
if ( random() % RANDOM_DIG_PROB == 0 )
(void) try_dig( a, 0 );
else if ( random() % RANDOM_TURN_PROB == 0 )
turn( a );
else
{
behave( a, B_WANDERING, T_WANDERING );
move( a );
}
break;
case B_CARRYING:
if ( random() % RANDOM_DROP_PROB == 0 )
drop( a );
else
{
behave( a, B_CARRYING, T_CARRYING );
move( a );
}
break;
case B_PANIC:
if ( random() % CALM_PROB == 0 )
{
behave( a, B_WANDERING, T_WANDERING );
}
else
{
behave( a, B_PANIC, T_PANIC );
move( a );
}
break;
}
}
}
}
}
static void
move( a )
int a;
{
int x, y, i, d, nx, ny, fx, fy;
x = ants[a].x;
y = ants[a].y;
d = ants[a].dir;
nx = x + dx[d];
ny = y + dy[d];
if ( nx < 0 || nx >= world_w || ny < 0 || ny >= world_h )
{
/* Hit an edge. Turn. */
turn( a );
return;
}
if ( world[ny][nx] != E_AIR )
{
/* Hit dirt or sand. Dig? */
if ( ants[a].behavior == B_WANDERING && ants[a].y >= surface &&
random() % CONCAVE_BELOW_DIG_PROB == 0 )
/* Yes, try digging. */
(void) try_dig( a, 1 );
else
/* Nope, no digging. Turn. */
turn( a );
return;
}
/* We can move forward. But first, check footing. */
fx = nx + dx[foot_dir[d]];
fy = ny + dy[foot_dir[d]];
if ( fx >= 0 && fx < world_w && fy >= 0 && fy < world_h &&
world[fy][fx] == E_AIR )
{
/* Whoops, we're over air. Move into the air and turn towards
** the feet. But first, see if we should drop.
*/
if ( ants[a].behavior == B_CARRYING && ants[a].y < surface &&
random() % CONVEX_ABOVE_DROP_PROB == 0 )
drop( a );
nx = fx;
ny = fy;
ants[a].dir = foot_dir[d];
}
/* Ok. */
invalidate_ant( a );
ants[a].x = nx;
ants[a].y = ny;
invalidate_ant( a );
}
static void
turn( a )
int a;
{
int n, d;
int ok_dirs[N_DIRS];
/* First check if turning "up" is ok. */
d = back_dir[ants[a].dir];
if ( legal_dir( a, d ) )
ants[a].dir = d;
else
{
/* Make a list of the legal directions. */
n = 0;
for ( d = 0; d < N_DIRS; ++d )
{
if ( d != ants[a].dir && legal_dir( a, d ) )
{
ok_dirs[n] = d;
++n;
}
}
if ( n != 0 )
{
/* Choose a random legal direction. */
ants[a].dir = ok_dirs[random() % n];
}
else
{
/* No legal directions to turn? Trapped! If we're carrying,
** drop, then turn randomly. Perhaps we can dig ourselves out.
*/
if ( ants[a].behavior == B_CARRYING )
drop( a );
ants[a].dir = random() % N_DIRS;
}
}
invalidate_ant( a );
}
static int
legal_dir( a, d )
int a, d;
{
int nx, ny;
/* Check that there's air ahead. */
nx = ants[a].x + dx[d];
ny = ants[a].y + dy[d];
if ( nx < 0 || nx >= world_w || ny < 0 || ny >= world_h ||
world[ny][nx] != E_AIR )
return 0;
/* Check that there's solid footing. */
nx = ants[a].x + dx[foot_dir[d]];
ny = ants[a].y + dy[foot_dir[d]];
if ( nx >= 0 && nx < world_w && ny >= 0 && ny < world_h &&
world[ny][nx] == E_AIR )
return 0;
return 1;
}
static int
try_dig( a, forward )
int a, forward;
{
int x, y;
if ( forward )
{
x = ants[a].x + dx[ants[a].dir];
y = ants[a].y + dy[ants[a].dir];
}
else
{
x = ants[a].x + dx[foot_dir[ants[a].dir]];
y = ants[a].y + dy[foot_dir[ants[a].dir]];
}
if ( x >= 0 && x < world_w && y >= 0 && y < world_h &&
world[y][x] != E_AIR )
{
world[y][x] = E_AIR;
invalidate( x, y, 1, 1 );
loosen_neighbors( x, y );
behave( a, B_CARRYING, T_CARRYING );
return 1;
}
else
return 0;
}
static void
loosen_neighbors( xc, yc )
int xc, yc;
{
int x, y;
for ( y = yc + 1; y >= yc - 1; --y )
for ( x = xc - 1; x <= xc + 1; ++x )
if ( x >= 0 && x < world_w && y >= 0 && y < world_h &&
world[y][x] == E_SAND )
loosen_one( x, y );
}
static void
loosen_one( x, y )
int x, y;
{
int i;
/* Check if there's already loose sand at this location. */
for ( i = 0; i < num_falling_sands; ++i )
if ( falling_sands[i].active &&
x == falling_sands[i].x && y == falling_sands[i].y )
return;
/* Check if there's room to store the new sand. */
if ( num_falling_sands == max_falling_sands )
{
if ( max_falling_sands == 0 )
{
max_falling_sands = 20;
falling_sands = (falling_sand*) malloc(
(unsigned) ( max_falling_sands * sizeof(falling_sand) ) );
}
else
{
max_falling_sands *= 2;
falling_sands = (falling_sand*) realloc(
(char*) falling_sands,
(unsigned) ( max_falling_sands * sizeof(falling_sand) ) );
}
if ( falling_sands == (falling_sand*) 0 )
{
(void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 );
}
}
/* Add it. */
falling_sands[num_falling_sands].x = x;
falling_sands[num_falling_sands].y = y;
falling_sands[num_falling_sands].active = 1;
++num_falling_sands;
}
static void
drop( a )
int a;
{
world[ants[a].y][ants[a].x] = E_SAND;
invalidate( ants[a].x, ants[a].y, 1, 1 );
loosen_one( ants[a].x, ants[a].y );
behave( a, B_WANDERING, T_WANDERING );
}
static void
behave( a, behavior, timer )
int a, behavior, timer;
{
ants[a].behavior = behavior;
ants[a].timer = 3 * timer / 4 + random() % timer / 2;
}
static void
sand_fall()
{
int i, x, y, gotone, tipl, tipr;
gotone = 0;
for ( i = 0; i < num_falling_sands; ++i )
if ( falling_sands[i].active )
{
gotone = 1;
x = falling_sands[i].x;
y = falling_sands[i].y;
if ( y + 1 >= world_h )
{
/* Hit bottom - done falling and no compaction possible. */
falling_sands[i].active = 0;
continue;
}
/* Drop the sand onto the next lower sand or dirt. */
if ( world[y + 1][x] == E_AIR )
{
falling_sands[i].y = y + 1;
world[y][x] = E_AIR;
world[falling_sands[i].y][falling_sands[i].x] = E_SAND;
invalidate( x, y, 1, 1 );
invalidate( falling_sands[i].x, falling_sands[i].y, 1, 1 );
loosen_neighbors( x, y );
continue;
}
/* Tip over an edge? */
tipl = ( x - 1 >= 0 && world[y + 1][x - 1] == E_AIR &&
y + 2 < world_h && world[y + 2][x - 1] == E_AIR );
tipr = ( x + 1 < world_w && world[y + 1][x + 1] == E_AIR &&
y + 2 < world_h && world[y + 2][x + 1] == E_AIR );
if ( tipl || tipr )
{
if ( tipl && tipr )
{
if ( random() % 2 == 0 )
falling_sands[i].x = x - 1;
else
falling_sands[i].x = x + 1;
}
else if ( tipl )
falling_sands[i].x = x - 1;
else if ( tipr )
falling_sands[i].x = x + 1;
falling_sands[i].y = y + 1;
world[y][x] = E_AIR;
world[falling_sands[i].y][falling_sands[i].x] = E_SAND;
invalidate( x, y, 1, 1 );
invalidate( falling_sands[i].x, falling_sands[i].y, 1, 1 );
loosen_neighbors( x, y );
continue;
}
/* Found the final resting place. */
falling_sands[i].active = 0;
/* Compact sand into dirt. */
for ( i = 0; y + 1 < world_h && world[y+1][x] == E_SAND; ++y, ++i )
;
if ( i >= COMPACT )
{
world[y][x] = E_DIRT;
invalidate( x, y, 1, 1 );
}
}
if ( ! gotone )
num_falling_sands = 0;
}